import oss2
import json
import time
import base64
import hmac
import hashlib
import datetime
from typing import Dict, Any, Optional
from aliyunsdkcore.client import AcsClient
from aliyunsdksts.request.v20150401 import AssumeRoleRequest
from aliyunsdkcore.auth.credentials import AccessKeyCredential
from alibabacloud_tea_openapi.models import Config
from alibabacloud_sts20150401.client import Client as Sts20150401Client
from alibabacloud_sts20150401 import models as sts_20150401_models
from alibabacloud_credentials.client import Client as CredentialClient

import os
from dotenv import load_dotenv

load_dotenv()

class OSSService:
    def __init__(self):
        # 从环境变量获取配置
        self.access_key_id = os.getenv('ALIYUN_ACCESS_KEY_ID')
        self.access_key_secret = os.getenv('ALIYUN_ACCESS_KEY_SECRET')
        self.role_arn = os.getenv('ALIYUN_ROLE_ARN')
        self.bucket_name = os.getenv('ALIYUN_OSS_BUCKET_NAME')
        self.endpoint = os.getenv('ALIYUN_OSS_ENDPOINT')
        self.region = os.getenv('ALIYUN_REGION', 'cn-hangzhou')
        
        # 验证必要配置
        if not all([self.access_key_id, self.access_key_secret, self.role_arn, self.bucket_name, self.endpoint]):
            raise ValueError("阿里云OSS配置不完整，请检查环境变量")
        
        # 初始化ACS客户端
        self.acs_client = AcsClient(
            self.access_key_id,
            self.access_key_secret,
            self.region
        )
    
    def generate_sts_token(self, user_id: int, duration_seconds: int = 3600) -> Dict[str, Any]:
        """
        生成STS临时访问凭证
        
        Args:
            user_id: 用户ID，用于生成唯一的会话名称
            duration_seconds: 凭证有效期（秒），默认1小时
            
        Returns:
            包含STS凭证的字典
        """
        try:
            # 创建AssumeRole请求
            request = AssumeRoleRequest.AssumeRoleRequest()
            request.set_RoleArn(self.role_arn)
            request.set_RoleSessionName(f"user_{user_id}_{int(time.time())}")
            request.set_DurationSeconds(duration_seconds)
            """
            # 设置策略，限制用户只能上传到指定目录
            policy = {
                "Version": "1",
                "Statement": [
                    {
                        "Effect": "Allow",
                        "Action": [
                            "oss:PutObject",
                            "oss:GetObject",
                            "oss:ListObjects"
                        ],
                        "Resource": [
                            f"acs:oss:*:*:{self.bucket_name}",
                            f"acs:oss:*:*:{self.bucket_name}/*"
                        ],
                        "Condition": {
                            "StringEquals": {
                                "oss:Prefix": f"uploads/{user_id}/"
                            }
                        }
                    }
                ]
            }
            
            request.set_Policy(json.dumps(policy))"""
            
            # 发送请求
            response = self.acs_client.do_action_with_exception(request)
            response_dict = json.loads(response.decode('utf-8'))
            
            # 提取凭证信息
            credentials = response_dict['Credentials']
            
            return {
                'success': True,
                'data': {
                    'access_key_id': credentials['AccessKeyId'],
                    'access_key_secret': credentials['AccessKeySecret'],
                    'security_token': credentials['SecurityToken'],
                    'expiration': credentials['Expiration'],
                    'bucket_name': self.bucket_name,
                    'endpoint': self.endpoint,
                    'region': self.region,
                    'upload_prefix': "cosmetics/"
                }
            }
            
        except Exception as e:
            return {
                'success': False,
                'error': f'生成STS凭证失败: {str(e)}'
            }
    
    def get_upload_url(self, user_id: int, filename: str, content_type: str = None) -> Dict[str, Any]:
        """
        获取文件上传URL
        
        Args:
            user_id: 用户ID
            filename: 文件名
            content_type: 文件类型（可选）
            
        Returns:
            包含上传URL的字典
        """
        try:
            # 生成STS凭证
            sts_result = self.generate_sts_token(user_id)
            if not sts_result['success']:
                return sts_result
            
            sts_data = sts_result['data']
            
            # 生成唯一的文件名
            timestamp = int(time.time())
            file_extension = os.path.splitext(filename)[1]
            unique_filename = f"{timestamp}_{filename}"
            object_key = f"{sts_data['upload_prefix']}{unique_filename}"
            
            # 使用STS凭证创建OSS客户端
            auth = oss2.StsAuth(
                sts_data['access_key_id'],
                sts_data['access_key_secret'],
                sts_data['security_token']
            )
            
            bucket = oss2.Bucket(auth, sts_data['endpoint'], sts_data['bucket_name'])
            
            # 生成预签名URL
            url = bucket.sign_url('PUT', object_key, 3600)
            
            return {
                'success': True,
                'data': {
                    'upload_url': url,
                    'object_key': object_key,
                    'filename': unique_filename,
                    'content_type': content_type,
                    'expires_in': 3600,
                    'sts_credentials': sts_data
                }
            }
            
        except Exception as e:
            return {
                'success': False,
                'error': f'生成上传URL失败: {str(e)}'
            }
    
    def list_user_files(self, user_id: int, prefix: str = None) -> Dict[str, Any]:
        """
        列出用户上传的文件
        
        Args:
            user_id: 用户ID
            prefix: 文件前缀（可选）
            
        Returns:
            文件列表
        """
        try:
            # 使用主账号凭证列出文件
            auth = oss2.Auth(self.access_key_id, self.access_key_secret)
            bucket = oss2.Bucket(auth, self.endpoint, self.bucket_name)
            
            # 构建前缀
            user_prefix = f"uploads/{user_id}/"
            if prefix:
                user_prefix += prefix
            
            files = []
            for obj in oss2.ObjectIterator(bucket, prefix=user_prefix):
                files.append({
                    'key': obj.key,
                    'size': obj.size,
                    'last_modified': obj.last_modified.isoformat(),
                    'etag': obj.etag
                })
            
            return {
                'success': True,
                'data': {
                    'files': files,
                    'count': len(files)
                }
            }
            
        except Exception as e:
            return {
                'success': False,
                'error': f'获取文件列表失败: {str(e)}'
            }
    
    def delete_file(self, object_key: str) -> Dict[str, Any]:
        """
        删除文件
        
        Args:
            object_key: 文件在OSS中的键
            
        Returns:
            删除结果
        """
        try:
            auth = oss2.Auth(self.access_key_id, self.access_key_secret)
            bucket = oss2.Bucket(auth, self.endpoint, self.bucket_name)
            
            bucket.delete_object(object_key)
            
            return {
                'success': True,
                'message': '文件删除成功'
            }
            
        except Exception as e:
            return {
                'success': False,
                'error': f'删除文件失败: {str(e)}'
            }

    def get_sts_signature(self, user_id: int) -> Dict[str, Any]:
        """
        处理生成签名信息的请求，执行相关逻辑流程，包括获取环境变量、创建策略、构造待签名字符串、计算签名等操作，
        并返回生成的签名信息。
        :return: JSON格式的响应，包含签名信息的字典，格式如下：
        {
            "policy": "policy字符串",
            "x-oss-signature-version": "OSS4-HMAC-SHA256",
            "x-oss-credential": "accesskeyid/日期/cn-hangzhou/oss/aliyun_v4_request",
            "x-oss-date": "请求时间",
            "signature": "签名",
            "security_token": "安全令牌"
        }
        """

        # 自定义会话名称
        role_session_name = f"user_{user_id}_{int(time.time())}"  

        # 指定过期时间，单位为秒
        expire_time = 3600  



        # 初始化配置，直接传递凭据
        config = Config(
            region_id=self.region,
            access_key_id=self.access_key_id,
            access_key_secret=self.access_key_secret
        )

        # 创建 STS 客户端并获取临时凭证
        sts_client = Sts20150401Client(config=config)
        assume_role_request = sts_20150401_models.AssumeRoleRequest(
            role_arn=self.role_arn,
            role_session_name=role_session_name
        )
        response = sts_client.assume_role(assume_role_request)
        token_data = response.body.credentials.to_map()

        # 使用 STS 返回的临时凭据
        sts_accesskeyid = token_data['AccessKeyId']
        sts_accesskeysecret = token_data['AccessKeySecret']
        security_token = token_data['SecurityToken']

        now = int(time.time())

        # 将时间戳转换为datetime对象
        dt_obj = datetime.datetime.utcfromtimestamp(now)
        # 在当前时间增加3小时，设置为请求的过期时间
        dt_obj_plus_3h = dt_obj + datetime.timedelta(hours=3)

        # 请求时间
        dt_obj_1 = dt_obj.strftime('%Y%m%dT%H%M%S') + 'Z'
        # 请求日期
        dt_obj_2 = dt_obj.strftime('%Y%m%d')
        # 请求过期时间
        expiration_time = dt_obj_plus_3h.strftime('%Y-%m-%dT%H:%M:%S.000Z')

        # 步骤1：创建policy。
        # 示例policy表单域只列举必填字段，如有其他需求可参考签名版本4文档：https://help.aliyun.com/zh/oss/developer-reference/signature-version-4-recommend
        policy = {
            "expiration": expiration_time,
            "conditions": [
                {"bucket": self.bucket_name},   #请将<bucket_name>替换为您的实际Bucket名称
                {"x-oss-signature-version": "OSS4-HMAC-SHA256"},
                {"x-oss-credential": f"{sts_accesskeyid}/{dt_obj_2}/{self.region}/oss/aliyun_v4_request"},    #请将<cn-hangzhou>替换为您的实际Bucket所处地域，例如北京地域为：cn-beijing
                {"x-oss-security-token": security_token},
                {"x-oss-date": dt_obj_1},
            ]
        }
        policy_str = json.dumps(policy).strip()

        # 步骤2：构造待签名字符串（StringToSign）
        stringToSign = base64.b64encode(policy_str.encode()).decode()

        # 步骤3：计算SigningKey
        dateKey = hmacsha256(("aliyun_v4" + sts_accesskeysecret).encode(), dt_obj_2)
        dateRegionKey = hmacsha256(dateKey, self.region)    #请将<cn-hangzhou>替换为您的实际Bucket所处地域，例如北京地域为：cn-beijing
        dateRegionServiceKey = hmacsha256(dateRegionKey, "oss")
        signingKey = hmacsha256(dateRegionServiceKey, "aliyun_v4_request")

        # 步骤4：计算Signature
        result = hmacsha256(signingKey, stringToSign)
        signature = result.hex()

        return {
            "policy": stringToSign,
            "x_oss_signature_version": "OSS4-HMAC-SHA256",
            "x_oss_credential": f"{sts_accesskeyid}/{dt_obj_2}/{self.region}/oss/aliyun_v4_request",    #请将<cn-hangzhou>替换为您的实际Bucket所处地域，例如北京地域为：cn-beijing
            "x_oss_date": dt_obj_1,
            "signature": signature,
            "security_token": security_token,
            "upload_prefix": "cosmetics/"
        }

def hmacsha256(key, data):
    """
    计算HMAC-SHA256哈希值的函数

    :param key: 用于计算哈希的密钥，字节类型
    :param data: 要进行哈希计算的数据，字符串类型
    :return: 计算得到的HMAC-SHA256哈希值，字节类型
    """
    try:
        mac = hmac.new(key, data.encode(), hashlib.sha256)
        hmacBytes = mac.digest()
        return hmacBytes
    except Exception as e:
        raise RuntimeError(f"Failed to calculate HMAC-SHA256 due to {e}")



# 创建全局实例
oss_service = OSSService() 